'''
Created on Nov 15, 2010

@author: dexen
'''


from dexen.system.common.meta import TaskMeta
from dexen.system.common.states import MasterState, M_RUNNING, M_FINISHED
from dexen.system.node.master.mgrs.ind_mgr import IndMgr
from dexen.system.node.master.mgrs.task_mgr import TaskMgr
from dexen.system.common.transact import MasterRecvTransact, Transaction,\
    RecvTransact, SlaveSendTransact, SlaveRecvTransact
from dexen.system.common.base import Individual



#TODO: keeping track of the # of processing inds or transactions for each task 

class StateMgr(object):
    def __init__(self):
        self.ind_mgr = IndMgr()
        self.task_mgr = TaskMgr(self.ind_mgr)
        self.trans_id = 1
        self.state = M_RUNNING
        self.cur_time = 1 # refers to action id
        self.slave_data = {}
    
    def set_slave_data(self, name, value):
        self.slave_data[name] = value
     
    def get_s_transact(self):
        task_meta = self.task_mgr.avail_task()
        if task_meta == None: return None
        assert isinstance(task_meta, TaskMeta)
        inds = self.task_mgr.get_inds(task_meta.name)
        transact = SlaveSendTransact(self.trans_id, task_meta, inds)
        self.trans_id += 1
        return transact
    
    def _commit_inds(self, transact):
        """Merge inds into the individual database
        """
        assert isinstance(transact, SlaveRecvTransact)
        updated_inds = []
        for ind in transact.get_inds():
            updated_ind = self.ind_mgr.set_ind(ind)
            updated_inds.append(updated_ind)
        transact.set_inds(updated_inds)

    def _update_inds(self, transact):
        assert isinstance(transact, Transaction)
        updated_inds = []
        for ind in transact.get_inds():
            assert isinstance(ind, Individual)
            updated_inds.append(self.ind_mgr.get_ind(ind.__id__))
        transact.set_inds(updated_inds)

    def _update_inds_state(self, inactive_ids, reactive_ids=[]):
        for id in inactive_ids:
            self.ind_mgr.deactivate_ind(id)
        for id in reactive_ids:
            self.ind_mgr.reactivate_ind(id)

    def _strip_archive_data(self, transact):
        assert isinstance(transact, Transaction)
        assert isinstance(transact, RecvTransact)
        for ind in transact.get_inds():
            assert isinstance(ind, Individual)
            self.ind_mgr.strip_ind_archive(ind.__id__)

    def set_s_transact(self, transact, sent_transact):
        assert isinstance(transact, SlaveRecvTransact)
        assert isinstance(sent_transact, SlaveSendTransact)
        self._commit_inds(transact)
        self._update_inds(sent_transact)
        self.task_mgr.update_slave(transact, sent_transact)
        inactive_ids = self.task_mgr.get_inactive_ids()
        self._update_inds_state(inactive_ids)
        self._update_inds(transact)
        self._strip_archive_data(transact)
    
    def set_m_transact(self, transact):
        assert isinstance(transact, MasterRecvTransact)
        self.task_mgr.update_master(transact)
        inactive_ids = self.task_mgr.get_inactive_ids()
        reactive_ids = self.task_mgr.get_reactive_ids()
        self._update_inds_state(inactive_ids, reactive_ids)
        self._update_inds(transact)
        self._strip_archive_data(transact)
    
    def recover_s_transact(self, transact, sent_transact):
        assert isinstance(transact, SlaveRecvTransact)
        self._update_inds(sent_transact)
        self.task_mgr.recover_slave(transact, sent_transact)
        inactive_ids = self.task_mgr.get_inactive_ids()
        self._update_inds_state(inactive_ids)
        self._update_inds(transact)
    
    def get_master_state(self):
        print "StateMgr.get_master_state:", self.task_mgr
        task_states = self.task_mgr.get_task_states()
        master_state = MasterState(task_states, self.ind_mgr.num_inds(), self.slave_data, self.state)
        return master_state
    
    def finish(self):
        self.state = M_FINISHED
    
    def has_finished(self):
        return self.state == M_FINISHED
    
    def set_cur_time(self, act_id):
        self.cur_time = act_id
        self.ind_mgr.set_cur_time(act_id)

    def get_task_mgr(self):
        return self.task_mgr
    
    def get_ind_mgr(self):
        return self.ind_mgr
